Skip to content

jaguar3: 8822C 40 MHz inject — DISDATAFB=1 and a wide-channel IGI floor on the C die - #484

Open
gilankpam wants to merge 6 commits into
OpenIPC:masterfrom
gilankpam:pr/8822c-ht40-tx
Open

gilankpam wants to merge 6 commits into
OpenIPC:masterfrom
gilankpam:pr/8822c-ht40-tx

Conversation

@gilankpam

@gilankpam gilankpam commented Oct 11, 2026 •

Copy link
Copy Markdown
Contributor

The problem

The RTL8812CU (8822C) could not air a single 40 MHz injected frame on most 5 GHz channels, though the same card does HT40 under mainline rtw88. On 144(-), 36(+) and 149(+), bulk-OUT accepted 257 frames and then failed with rc=-7, and nothing went on air. At the abort the TX FIFO was full (LQ 64/0 PUB 1745/19): the MAC never completed the first 40 MHz frame. HT20 was unaffected.

There were two independent faults, and fixing either alone is not enough.

The change

Both parts apply only to the 8822C. The 8822E's descriptor and DIG are byte-identical to before.

  • DISDATAFB=1 on 40/80 MHz inject descriptors (fill_data_tx_desc_8822c, new dis_data_fb argument).
  • A DIG floor at 40/80 MHz (DIG_MIN_8822C_WIDE 0x26, PhydmRuntimeJaguar3::set_igi_floor).
    • On a quiet channel the receiver parks below IGI 0x24, in a gain state where the secondary 20 MHz never reads idle, so the MAC holds every 40 MHz frame.
    • Busy channels self-heal, because received frames re-run the AGC. That is why the fault hides on a busy bench channel.
    • The floor is armed for the session by the device's first >= 40 MHz frame. Init, InitWrite and Stop() clear the arm.
    • From then on it follows the tuned width, re-applied under _reg_mu by every tune (Init / InitWrite / SetMonitorChannel / FastRetune / FastSetBandwidth). It is on at 40/80 MHz. At a narrower width it drops, with IGI clamped straight back into the normal DIG window.
    • A device that only receives at 40 MHz is never armed. That includes a CU ground station sending 20 MHz control frames, which keeps its normal floor.
    • Arming takes _reg_mu once per session and never on the steady-state send path. The flag is published only after the floor is applied, so neither a racing retune nor a concurrent sender can skip it.

Record, including the dead ends (firmware RA_INFO in every layout, firmware version, IQK, TX power, CCA, AGC lower bound): docs/experiments/8822c-ht40-tx-stall.md.

Measured

txdemo on the CU, with an 8812EU witness on rxdemo at the same channel and width. 1 kB QoS-data frames, MCS3/40/LDPC, 50 s per run, no knobs. "Aired" means CRC-ok frames the witness decoded with bw:1.

channel width submitted failed aired (witness)
144(-) 40 20552 0 20500 MCS3 bw:1
52(+) 40 20525 0 20425 MCS3 bw:1
149(+) 40 20590 0 20477 MCS3 bw:1
36(+) 40 20483 0 19723 MCS3 bw:1
144(-) 20 20679 0 20629 MCS3 bw:0 (IGI stays 0x20)

Before the change, the same 40 MHz runs aired nothing.

How each half was isolated:

  • With only DISDATAFB=1, 36(+) worked but the silent channels 52/100/120/144 hung after 0–130 frames.
  • On silent 144, pinning IGI at 0x35, 0x2c, 0x26 or 0x24 gave about 20.6k frames with 0 failures each. 0x22 gave 22 frames, then a hang.
  • On silent 144, a second card sending MCS0/20 at 50 fps also cleared the hang (20.4k frames, 0 failed).
  • With the floor armed, a 20 MHz frame into a 40 MHz tune ran 9928 / 0 at IGI 0x20, and an RX-only session at 40 MHz stayed at 0x20.

The counterpart: the floor costs idle-channel RX sensitivity at 40/80 MHz on a CU that transmits wide. That cost is estimated from the 1 dB IGI step, not measured: 6 dB over the DFS floor 0x20, and 4–8 dB over the 0x1e..0x22 coverage window on a non-DFS channel. Each row is a single 50 s run. The effect size (0 vs ~20k) is far outside probe-to-probe noise, but the 36(+) row's lower witness count (96.3% vs ~99.5% elsewhere) was not repeated.

Re-test on the final code (both adapters local, near-field, silent channels confirmed by an 8 s scan beforehand, 30 s per cell):

cell DUT -> witness tune frames submitted failed witness IGI
144(-) CU -> EU 40 MCS3/40 14161 0 14109 MCS3 bw:1 0x26
149(+) CU -> EU 40 MCS3/40 14146 0 14094 MCS3 bw:1 0x26
144 CU -> EU 20 MCS3/40 14282 0 0 (expected) 0x20 (unarmed)
144 CU -> EU 20 MCS3/20 14198 0 14142 MCS3 bw:0 0x20
144(-) EU -> CU 40 MCS3/40 14455 0 14321 MCS3 bw:1 0x20
  • In the last row the 8822E transmits HT40 unchanged. The CU, receiving only at 40 MHz as the witness, also stays at 0x20.
  • The send rate is gap-limited (~460 fps), so these runs do not measure the removed per-frame lock directly. The third row checks only that the floor stays unarmed.

After the review (f87521b), same bench, silent 144:

cell tune frames submitted failed witness IGI
CU -> EU, 144(-) 40 MCS3/40 14117 0 14067 MCS3 bw:1 0x26
CU -> EU, 144(-) 40 MCS3/20 (DISDATAFB=0) 14163 0 14111 MCS3 bw:0 0x20
CU -> EU, 144 20 MCS3/20 14285 0 14232 MCS3 bw:0 0x20
EU -> CU, 144(-) 40 MCS3/40 14456 0 14406 MCS3 bw:1 0x20

Retry ladder, tests/retry_ladder_probe.sh shape: unACKed unicast, MCS3, limit 8.

Headless: ctest full 78/78, without Jaguar3 78/78, 8822C-only 69/69.

Not covered

  • 80 MHz inherits the floor untested.
  • 2.4 GHz HT40 (ch 6) drained on the TX side, but the witness received none of it. This is unexplained, and the change does not address it.
  • The RX-sensitivity cost above was not measured.
  • Not exercised on air: the IGI clamp when the floor drops (no demo changes width mid-session), and the retune/first-frame race, which rests on lock ordering.
  • Range: the re-test witness was near-field, so those runs are pass/fail, not range numbers. The original table used a separate ground-station 8812EU.

🤖 Generated with Claude Code

gilankpam and others added 4 commits October 11, 2026 23:53
The RTL8812CU could not air a single 40 MHz frame on most channels while
the same card does HT40 under mainline rtw88. Two faults, both gated on
the 8822C so the 8822E's descriptor and DIG stay byte-identical:

- The inject descriptor carried DISDATAFB=0 (the Jaguar1 mirror). With
  USE_RATE=1 the MAC's per-frame rate-fallback lookup consults the fw's
  per-MACID entry, which an injecting MACID lacks; on the 8822C that
  wedges the first 40 MHz frame and the TX FIFO fills. The device now
  passes dis_data_fb=1 on the C die (the vendor's fixed-rate path does).
- On a quiet channel the receiver parks below IGI 0x24 in a gain state
  where the secondary 20 MHz never reads idle, so the MAC holds every
  40 MHz frame; busy channels self-heal because received frames re-run
  the AGC. DIG_MIN_8822C_WIDE 0x26 is pushed into the phydm runtime after
  every tune (Init/InitWrite/SetMonitorChannel/FastRetune/
  FastSetBandwidth) at 40/80 MHz and honoured by every DIG pass. Costs
  ~6 dB of idle-channel RX sensitivity at those widths on the CU.

Measured on 144(-)/52(+)/149(+)/36(+): ~20.5k frames per 50 s run, 0
failures, all received at MCS3 bw 40; HT20 unchanged. Record:
docs/experiments/8822c-ht40-tx-stall.md.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PbfYcAbwocHjWbXn9q8tzB
… sent

The floor costs ~6 dB of idle-channel RX sensitivity, which a device that
only receives at 40 MHz — a CU ground station, whose own control frames
are 20 MHz — pays for nothing. Arm it from build_tx_block on the first
>= 40 MHz descriptor (one _reg_mu take, the FastRetune precedent), keep it
across tunes while the width stays wide, drop it at a narrow width.

Bench, ch 144 silent, no knobs: HT40 TX 9091 frames / 0 failed with IGI
0x26 after arming; 20 MHz frames into the 40 MHz tune 9928 / 0 at IGI
0x20; RX-only at 40 MHz stays at 0x20.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PbfYcAbwocHjWbXn9q8tzB
…cost as an estimate

Review follow-up to the 8822C HT40 inject fix.

- A 40 MHz frame into a 20 MHz tune armed the floor and apply_igi_floor_locked
  disarmed it again at the narrow width, so every such frame re-took _reg_mu on
  the send path (txdemo hits it with DEVOURER_TX_RATE=.../40 and no
  DEVOURER_HOP_BW). Arming now requires the tune to be >= 40 MHz too.
- Stop() clears the arm, so a later Init on the same device starts without
  the floor.
- The RX-sensitivity cost was quoted as "~6 dB". It is arithmetic from the
  1 dB IGI step, not a measurement, and 6 dB holds only over the DFS floor;
  on a non-DFS channel (36 and 149 in the result table) it is 4-8 dB over the
  coverage window. Header, experiment doc and CLAUDE.md now say so.
- src/jaguar3/CLAUDE.md keeps the fact and points at the headers and the
  experiment doc instead of restating them.

No hardware re-run: the arming change only removes a narrow-tune case that
never applied the floor anyway. ctest 81/81.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Five 30 s cells on silent 144(-)/149(+), both adapters local:
- CU HT40: 0 failed, witness 99.6 % at MCS3 bw:1, floor armed at 0x26.
- 40 MHz frames into a 20 MHz tune leave the floor unarmed (0x20).
- HT20 control: unchanged.
- 8822E HT40: 0 failed, 99.1 % at bw:1, IGI 0x20, as is the RX-only CU
  witness.

Near-field single runs. The send rate is gap-limited, so this does not
measure the removed per-frame lock.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@qodo-free-for-open-source-projects

Copy link
Copy Markdown

PR Summary by Qodo

Fix 8822C 40 MHz frame injection stalls

🐞 Bug fix 📝 Documentation 🕐 20-40 Minutes

Grey Divider

AI Description

• Disable descriptor rate fallback on 8822C to prevent injected 40 MHz frames from wedging.
Diagram

graph TD
  A["Injected frame"] --> B{"8822C die?"} -->|Yes| C["Disable fallback"] --> D{"Wide TX and tune?"} -->|Yes| E["Arm IGI floor"] --> F["DIG floor 0x26"] --> H["Reapply on retune"]
  B -->|No| G["Unchanged gain"]
  D -->|No| G
  H -->|Narrow or stop| G
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Firmware-managed fixed rate
  • ➕ Avoids the injected descriptor's rate-fallback interaction.
  • ➖ Fixes rate per MACID rather than per packet and does not resolve quiet-channel gain stalls.
2. Always-on wide-channel IGI floor
  • ➕ Simplifies floor lifecycle management.
  • ➖ Imposes the estimated RX-sensitivity cost on receive-only devices and wide-tuned devices sending only narrow frames.

Recommendation: Keep the chip-gated descriptor setting and TX-armed floor: the documented experiments show both are needed, while arming limits the gain trade-off to devices transmitting wide frames. Follow-up measurements should quantify RX sensitivity and verify 80 MHz behavior.

Files changed (7) +215 / -4

Bug fix (5) +87 / -3
FrameParserJaguar3.hMake TX-descriptor fallback disabling selectable +8/-2

Make TX-descriptor fallback disabling selectable

• Adds a 'dis_data_fb' argument to the 8822C-format descriptor builder and writes it to DISDATAFB. The default remains false, allowing the device caller to opt in only for the 8822C die.

src/jaguar3/FrameParserJaguar3.h

PhydmRuntimeJaguar3.cppApply and enforce a configurable DIG floor +10/-0

Apply and enforce a configurable DIG floor

• Adds immediate IGI raising when a floor is set and clamps subsequent DIG bounds to that floor. Clearing the floor restores the normal DIG bounds on later ticks.

src/jaguar3/PhydmRuntimeJaguar3.cpp

PhydmRuntimeJaguar3.hDefine the 8822C wide-channel IGI bound +20/-0

Define the 8822C wide-channel IGI bound

• Defines the 0x26 floor and exposes runtime floor state and its setter. Documents when the floor is armed and the estimated RX-sensitivity trade-off.

src/jaguar3/PhydmRuntimeJaguar3.h

RtlJaguar3Device.cppGate descriptor and DIG fixes to 8822C wide TX +40/-1

Gate descriptor and DIG fixes to 8822C wide TX

• Passes DISDATAFB only for 8822C and arms its IGI floor before the first wide frame into a wide tune. Reapplies the floor across initialization and retune paths, and disarms it on narrow tuning or stop.

src/jaguar3/RtlJaguar3Device.cpp

RtlJaguar3Device.hTrack device-level wide-TX floor arming +9/-0

Track device-level wide-TX floor arming

• Declares the floor-application helper and atomic arming state. Documents the receive-only and narrow-frame cases that must retain normal gain behavior.

src/jaguar3/RtlJaguar3Device.h

Documentation (2) +128 / -1
8822c-ht40-tx-stall.mdRecord the 8822C HT40 stall investigation +116/-0

Record the 8822C HT40 stall investigation

• Documents the FIFO stall, rejected hypotheses, evidence for both fixes, and witness-received frame counts. Calls out the estimated sensitivity cost and unverified 80 MHz and 2.4 GHz HT40 behavior.

docs/experiments/8822c-ht40-tx-stall.md

CLAUDE.mdDocument Jaguar3 wide-TX requirements +12/-1

Document Jaguar3 wide-TX requirements

• Adds the 8822C-only descriptor and DIG requirements and links the experiment record. Clarifies that existing interference measurements were taken at 20 MHz, not with the new wide-channel floor.

src/jaguar3/CLAUDE.md

gilankpam added a commit to gilankpam/devourer that referenced this pull request Oct 11, 2026
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@qodo-free-for-open-source-projects

qodo-free-for-open-source-projects Bot commented Oct 11, 2026 •

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0) 🎨 UX issues (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)

Grey Divider


Action required

1. Concurrent retuning can leave wide TX stuck ✓ Resolved
Description
build_tx_block() reads _channel.ChannelWidth before acquiring _reg_mu and does not recheck the
width after acquiring it. If a concurrent retune changes a narrow channel to a wide one between
those steps, the first wide frame skips floor arming and can encounter the quiet-channel stall this
change is meant to prevent.
Code

src/jaguar3/RtlJaguar3Device.cpp[2761]

+  const ChannelWidth_t tune_bw = _channel.ChannelWidth;
Evidence
The added predicate samples a non-atomic width without the lock, whereas bandwidth changes update it
under that lock. The inner locked block checks only the armed flag; it cannot recover when the
earlier sample was narrow.

src/jaguar3/RtlJaguar3Device.cpp[2761-2770]
src/jaguar3/RtlJaguar3Device.cpp[1587-1617]
src/jaguar3/RtlJaguar3Device.cpp[1620-1628]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The first wide transmission can miss floor arming when a retune changes the width concurrently with descriptor construction.
## Fix Focus Areas
- src/jaguar3/RtlJaguar3Device.cpp[2761-2770]
- src/jaguar3/RtlJaguar3Device.cpp[1587-1617]
## Recommended Fix
Read and validate the tuned width under the same synchronization used by retuning, including a recheck after acquiring the register lock. Keep a lock-free steady-state path if possible.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. 8822C retries no longer step down in rate at any width ✓ Resolved
Description
build_tx_block() passes dis_data_fb = (_variant == C8822C) regardless of frame width, so
RetryFallback::Default behaves like Off on the 8822C. When a unicast link uses a nonzero retry
limit, this also prevents 20 MHz frames from following the documented MCS3 x4 → MCS2 → 6M firmware
ladder, despite the claim that HT20 is unchanged, and makes the nearby default-fallback comment
inaccurate.
Code

src/jaguar3/RtlJaguar3Device.cpp[R2772-2777]

+  /* 8822C only: DISDATAFB=1 — see fill_data_tx_desc_8822c. The 8822E keeps
+   * the byte-identical descriptor it flies 40 MHz with. */
jaguar3::fill_data_tx_desc_8822c(
    out, static_cast<uint16_t>(frame_len), MRateToHwRate(fixed_rate), rate_id,
    bw_desc, sgi != 0, ldpc != 0, stbc, bmc, ndpa, data_sc, pwr_type,
-      pkt_offset);
+      pkt_offset, /*dis_data_fb=*/_variant == jaguar3::ChipVariant::C8822C);
Evidence
DeviceConfig.h documents firmware rate fallback as the 8822C default and DISDATAFB=1 as the Off
setting that pins retries to the descriptor rate. RtlJaguar3Device.cpp explicitly sets DISDATAFB=1
for RetryFallback::Off, with a comment saying the default leaves it at zero, but
build_tx_block() now supplies DISDATAFB=1 for every 8822C frame regardless of bandwidth. Thus the
descriptor uses the Off value for both settings, including on 20 MHz frames that do not need the
wide-channel stall fix.

src/DeviceConfig.h[82-97]
src/jaguar3/RtlJaguar3Device.cpp[2823-2830]
src/jaguar3/FrameParserJaguar3.h[176-181]
src/jaguar3/RtlJaguar3Device.cpp[2774-2777]
src/jaguar3/RtlJaguar3Device.cpp[2824-2831]
src/jaguar3/FrameParserJaguar3.h[175-181]
src/DeviceConfig.h[247-257]
src/DeviceConfig.h[283-288]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The 8822C now writes DISDATAFB=1 on every injected frame, disabling the firmware retry ladder documented for `RetryFallback::Default`, including on 20 MHz links. The retry-fallback override comment is consequently stale.
## Fix Focus Areas
- src/jaguar3/RtlJaguar3Device.cpp[2772-2777]
- src/jaguar3/RtlJaguar3Device.cpp[2823-2831]
- src/jaguar3/FrameParserJaguar3.h[175-181]
- src/DeviceConfig.h[82-97]
## Recommended Fix
Limit the forced setting to the widths that require it, for example `dis_data_fb = _variant == C8822C && (bwidth == CHANNEL_WIDTH_40 || bwidth == CHANNEL_WIDTH_80)`, while preserving explicit `RetryFallback::Off` behavior for other frames. If DISDATAFB=1 is necessary at every width, instead update the DeviceConfig.h note and retry-fallback override comment to state that the 8822C default no longer steps down, and correct the claim that HT20 is unchanged.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

3. Narrow-channel RX retains the wide gain ✓ Resolved
Description
set_igi_floor(0) removes the software bound but does not lower an IGI already raised to 0x26.
When an armed device switches to 20 MHz, its receiver retains that gain until a later DIG tick,
temporarily carrying the wide-channel sensitivity cost into the narrow channel.
Code

src/jaguar3/PhydmRuntimeJaguar3.cpp[R32-34]

+  _igi_floor = floor;
+  if (floor != 0 && get_igi() < floor)
+    set_igi(floor);
Evidence
The setter writes IGI only when enabling a nonzero floor. Narrow retunes pass zero to it, while the
normal DIG bounds are applied only by the periodically invoked tick.

src/jaguar3/PhydmRuntimeJaguar3.cpp[31-35]
src/jaguar3/PhydmRuntimeJaguar3.cpp[85-119]
src/jaguar3/RtlJaguar3Device.cpp[1609-1628]
src/jaguar3/RtlJaguar3Device.cpp[279-299]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Removing the wide floor leaves hardware IGI at the raised value until the next DIG tick.
## Fix Focus Areas
- src/jaguar3/PhydmRuntimeJaguar3.cpp[31-35]
- src/jaguar3/RtlJaguar3Device.cpp[1620-1628]
## Recommended Fix
When the floor is removed, immediately clamp hardware IGI to the applicable normal DIG range for the tuned channel rather than waiting for a periodic tick.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


4. Reinitialized receivers inherit TX gain ✓ Resolved
Description
Init() and InitWrite() apply the floor using _wide_tx_armed without clearing that flag for the
new bring-up. Reinitializing a previously armed device directly onto a wide channel therefore raises
its receiver to 0x26 before it transmits any wide frame in the new session.
Code

src/jaguar3/RtlJaguar3Device.cpp[133]

+  apply_igi_floor_locked(channel.ChannelWidth);
Evidence
Both bring-up paths support re-initialization and now apply the floor. Neither resets the flag
beforehand; the floor helper treats a retained true flag and a wide width as grounds to enable the
floor.

src/jaguar3/RtlJaguar3Device.cpp[85-133]
src/jaguar3/RtlJaguar3Device.cpp[886-961]
src/jaguar3/RtlJaguar3Device.cpp[1620-1628]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
A direct re-initialization can inherit a prior session’s wide-transmit floor.
## Fix Focus Areas
- src/jaguar3/RtlJaguar3Device.cpp[85-133]
- src/jaguar3/RtlJaguar3Device.cpp[886-961]
- src/jaguar3/RtlJaguar3Device.cpp[1620-1628]
## Recommended Fix
Clear the wide-transmit armed state at the start of each new Init and InitWrite session, before either path reapplies the floor.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Tip of the day
💡 Did you know, you can tweak Display settings with a live preview to see your comment before it ships

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment thread src/jaguar3/RtlJaguar3Device.cpp Outdated
Comment thread src/jaguar3/PhydmRuntimeJaguar3.cpp Outdated
Comment thread src/jaguar3/RtlJaguar3Device.cpp Outdated
Comment thread src/jaguar3/RtlJaguar3Device.cpp Outdated
…ssion-scoped floor arm

- DISDATAFB=1 was written on every 8822C frame, which silently turned the
  fw retry ladder (OpenIPC#362/OpenIPC#371, RetryFallback::Default) off at 20 MHz too.
  It now covers 40/80 MHz frames only. Measured on the CU, unACKed unicast
  MCS3 limit 8:
  - 20 MHz: MCS3 x4 > MCS2 > 6M restored, 1223/1224.
  - 40 MHz: pinned at MCS3, 1208/1209.
  - 20 MHz frames with DISDATAFB=0 on a 40 MHz tune: 14163 / 0 failed,
    so the wedge keys on frame width.
  DeviceConfig.h's RetryFallback note says the 8822C's wide frames have no
  ladder at either setting.
- The floor arm is now "this session has sent a wide frame" (_wide_tx_seen):
  - Set once, published after the floor is applied, cleared by Init,
    InitWrite and Stop.
  - The floor follows the tuned width, read under _reg_mu on both sides.
  - A retune racing the first wide frame can no longer skip it, and a
    re-Init no longer inherits it.
  - A 40 MHz frame on a 20 MHz tune still takes the lock once per session.
- Dropping the floor clamps IGI back into the channel's DIG window at once,
  instead of leaving 0x26 until the next tick. Keeping it at 0 touches no
  register, so the 8822E path is unchanged.

Not exercised on air: the drop clamp and the race. ctest 78/78. HT40 CU
and EU and HT20 cells re-run, 0 failed (experiment doc).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
gilankpam added a commit to gilankpam/devourer that referenced this pull request Oct 11, 2026
…ssion-scoped floor arm

- DISDATAFB=1 was written on every 8822C frame, which silently turned the
  fw retry ladder (OpenIPC#362/OpenIPC#371, RetryFallback::Default) off at 20 MHz too.
  It now covers 40/80 MHz frames only. Measured on the CU, unACKed unicast
  MCS3 limit 8:
  - 20 MHz: MCS3 x4 > MCS2 > 6M restored, 1223/1224.
  - 40 MHz: pinned at MCS3, 1208/1209.
  - 20 MHz frames with DISDATAFB=0 on a 40 MHz tune: 14163 / 0 failed,
    so the wedge keys on frame width.
  DeviceConfig.h's RetryFallback note says the 8822C's wide frames have no
  ladder at either setting.
- The floor arm is now "this session has sent a wide frame" (_wide_tx_seen):
  - Set once, published after the floor is applied, cleared by Init,
    InitWrite and Stop.
  - The floor follows the tuned width, read under _reg_mu on both sides.
  - A retune racing the first wide frame can no longer skip it, and a
    re-Init no longer inherits it.
  - A 40 MHz frame on a 20 MHz tune still takes the lock once per session.
- Dropping the floor clamps IGI back into the channel's DIG window at once,
  instead of leaving 0x26 until the next tick. Keeping it at 0 touches no
  register, so the 8822E path is unchanged.

Not exercised on air: the drop clamp and the race. ctest 78/78. HT40 CU
and EU and HT20 cells re-run, 0 failed (experiment doc).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
gilankpam added a commit to gilankpam/devourer that referenced this pull request Oct 11, 2026
…only, session-scoped floor) into the fork

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Folds the original table and both re-test sections into one: GS and local
benches in one table, superseded intermediate rows dropped, the retry
ladder and the limits stated once.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

@josephnef josephnef left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-ran the PR's cells on a second bench and the mechanism holds, but the two things the PR commits to — the 0x26 constant and the C-die-only gate — do not survive it. Request changes on those two; the code shape itself is fine.

Bench: LB-LINK WDN1300H (RTL8812CU, 0bda:c812, chip-id 0x13 cut 3) and a LB-LINK BL-M8812EU2 module (RTL8812EU, 0bda:a81a, rfe 21), near-field, no rtw88 loaded. Witness = rxdemo on the other adapter at the same channel/width, counting rx.txhit with bw:1. TX = txdemo, DEVOURER_TX_QOS_DATA=1 DEVOURER_TX_PAYLOAD_BYTES=1000, MCS3/40/LDPC, 2 ms gap, 20 s per cell, single run each. 144(-) and 100(+) were silent (8 s witness scan at 40 MHz: 0 frames). PR binary = c59a3a3; "both dies" rows = the same tree with the C8822C test removed from apply_igi_floor_locked and the arm in build_tx_block (DISDATAFB still C-only).

cell TX → witness ch floor submitted failed witness bw:1
master CU → EU 144(-) – 257 8 0
PR CU → EU 144(-) 0x26 (armed, logged) 257 8 0
PR, DEVOURER_DIS_CCA=1 CU → EU 144(-) 0x26 257 8 0
PR CU → EU 100(+) 0x26 308 8 51
PR CU → EU 36(+) (busy) 0x26 7842 0 7727
PR + 8822BU flooding MCS0/20 at 50 fps CU → EU 144(-) 0x26 7321 8 7064
PR, floor 0x2c CU → EU 144(-) 0x2c 1752 8 1495
PR, floor 0x35 CU → EU 144(-) 0x35 7851 0 7801
PR, floor 0x35 CU → EU 100(+) 0x35 7837 0 7787
PR (unchanged E path) EU → CU 144(-) none (IGI 0x20) 257 8 0
PR (unchanged E path) EU → CU 100(+) none 269 8 12
PR + flooder EU → CU 144(-) none 4811 343 4095
PR, floor on both dies, 0x2c EU → CU 144(-) 0x2c 7858 6 7794
PR, floor on both dies, 0x35 EU → CU 144(-) 0x35 7787 4 7725
PR, floor on both dies, 0x35 EU → CU 100(+) 0x35 7897 3 7844
PR CU → EU 144(-), MCS3/20 into the 40 tune 0x20 7857 0 7807 (bw:0)

In the flooder row the 8 failures are the last 8 sends, after the flooder's timeout ended ~2 s before the DUT's: the hang resumes the moment the channel goes quiet, which is the PR's model exactly.

1. DIG_MIN_8822C_WIDE = 0x26 is a one-unit calibration. On this CU the armed 0x26 floor (confirmed in the log) changes nothing against master on both silent channels; 0x2c airs ~1.5k frames then wedges; 0x35 clears it. Your unit's threshold sat at 0x22/0x24, this one's sits between 0x2c and 0x35. A hard constant one step above one unit's edge will ship as "40 MHz still dead" on the next unit. Either take the floor high enough to carry the units measured so far (with the sensitivity cost restated: 0x35 is 21 steps over the DFS floor, not 6), or expose it as a DeviceConfig field with that default, and in both cases say in the header and the experiment doc that the edge is per-unit and which units were measured. The vendor phydm has no bandwidth-raised bound to lean on here (phydm_dig_abs_boundary_decision pins DFS at DIG_MIN_DFS at every width), so this really is our own number.

2. The E die has the same stall on this bench. The PR's "8822E does HT40 unchanged" is true on your EU and false on this one: 257/8/0 on silent 144(-) and 100(+), by the unchanged E path, same shape as the CU, partially relieved by the flooder and cleared by the same floor at 0x2c and 0x35. So the quiet-channel half must not be gated on C8822C; arm and apply it on both Jaguar3 dies (DISDATAFB can stay C-only — the EU flew with DISDATAFB=0 and the floor). That also removes the "8822E's DIG is byte-identical" sentences from src/jaguar3/CLAUDE.md, DeviceConfig.h and the experiment doc.

3. Same stall with the demo's default 148-byte management frame (no DEVOURER_TX_QOS_DATA): 953 submitted / 8 failed / 0 witnessed at 40 MHz on both dies, master and PR alike, on silent 144(-). Not a separate fault as far as I can tell — just noting the frame shape doesn't matter.

Smaller points, no re-test needed:

  • FastRetune's fast path returns before apply_igi_floor_locked; fine today because the lean hop never rewrites 0x1d70 and the floor value doesn't depend on DFS-ness, but worth one line in the comment there so the next person doesn't "fix" it.
  • is_dfs_channel: channel > 14 && is redundant with >= 52.
  • The Init/InitWrite apply_igi_floor_locked calls can only ever clear a floor left in _phydm by a previous session (the flag was just reset). Say so in the comment; right now it reads as if they could arm one.
  • DeviceConfig.h line 290 runs well past 80 columns.
  • The 80 MHz cell here (CU → EU, 149(+), VHT1SS_MCS3/80, floor 0x26) was partial too: 470 / 10 / 209. Same open item as the PR says, not new.

Every row above is one run, so treat the 0x2c and the two partial rows as indicative; the 0-vs-7.8k rows are not inside any noise band.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants